#ifndef CONF_ATTRIBUTE_H_
#define CONF_ATTRIBUTE_H_

#include <boost/lexical_cast.hpp>
#include <boost/logic/tribool.hpp>

#include "node.h"
#include "connection.h"
#include "writer.h"

namespace gpyp {
namespace conf {

class Element;

class AttributeBase : public Node {
  friend class Element;
protected:
  sigc::signal<void> signal_loaded_;
  sigc::signal<void> signal_changed_;
public:
  inline sigc::signal<void> signal_loaded() {
    return signal_loaded_;
  }
  inline sigc::signal<void> signal_changed() {
    return signal_changed_;
  }

private:
  bool dirty_;

protected:
  virtual void on_changed();
  virtual void on_load(const char *value);
  virtual void on_save(Writer &writer) = 0;

  virtual bool on_get_dirty() const {
    return dirty_;
  }
  virtual void on_set_dirty(bool dirty) {
    dirty_ = dirty;
  }

public:
  virtual Glib::ustring get_string() const = 0;
  virtual void set_string(const Glib::ustring &str) = 0;

protected:
  AttributeBase(const Glib::ustring &name);
};
typedef boost::shared_ptr<AttributeBase> RefAttributeBase;

template<typename VT, typename DerivedClass>
class Attribute : public AttributeBase {
public:
  typedef VT ValueType;

protected:
  ValueType defval_;
  ValueType value_;

  ConnectionList<ValueType> conns_;

protected:
  void conns_on_changed(ValueType value) {
    if (!DerivedClass::equal(value_, value)) {
      value_ = value;
      on_changed();
    }
  }

  virtual void on_load(const char *value) {
    set_string(value);
    AttributeBase::on_load(value);
  }
  virtual void on_save(Writer &writer) {
    writer.write_attribute(get_name(), get_string());
  }

public:
  static bool equal(const ValueType &v1, const ValueType &v2) {
    return v1 == v2;
  }

  template<typename T>
  inline void attach(Glib::PropertyProxy<T> prop) {
    prop.set_value(get_value());
    conns_.attach(prop);
  }
  template<typename T>
  inline void attach(Glib::PropertyProxy_ReadOnly<T> prop) {
    conns_.attach(prop);
  }
  template<typename T>
  inline void attach(T *obj, const char *prop) {
    conns_.attach(obj, prop);
  }

  inline void detach(Glib::PropertyProxy_Base prop) {
    conns_.detach(prop);
  }
  template<typename T>
  inline void detach(T *obj, const char *prop) {
    conns_.detach(obj, prop);
  }

  const ValueType &get_value() const {
    return value_;
  }
  const ValueType &get_default_value() const {
    return defval_;
  }
  void set_value(const ValueType &value) {
    if (!DerivedClass::equal(value_, value)) {
      value_ = value;
      conns_.set_value(value_);
      on_changed();
    }
  }
  inline void revert_default() {
    set_value(defval_);
    set_dirty(false);
  }

  const ValueType &operator*() const {
    return value_;
  }
  const ValueType &operator=(const ValueType &value) {
    set_value(value);
    return value;
  }
  ValueType &operator=(ValueType &value) {
    set_value(value);
    return value;
  }


  virtual Glib::ustring get_string() const {
    return DerivedClass::to_string(get_value());
  }
  virtual void set_string(const Glib::ustring &str) {
    set_value(DerivedClass::to_value(str));
  }

  Attribute(const Glib::ustring &name, const ValueType &defval) :
    AttributeBase(name),
    defval_      (defval),
    value_       (defval_)
  {
    conns_.signal_changed()
      .connect(sigc::mem_fun(*this, &Attribute<ValueType, DerivedClass>::conns_on_changed));
  }
};

template<typename T>
class RefAttribute : public boost::shared_ptr<T> {
public:
  typedef typename T::ValueType ValueType;

  const ValueType &operator*() const {
    return boost::shared_ptr<T>::get()->get_value();
  }
  const ValueType &operator=(const ValueType &value) {
    boost::shared_ptr<T>::get()->set_value(value);
    return value;
  }
  ValueType &operator=(ValueType &value) {
    boost::shared_ptr<T>::get()->set_value(value);
    return value;
  }

  RefAttribute() {
  }
  RefAttribute(T *ptr) :
    boost::shared_ptr<T>(ptr)
  {
  }
  RefAttribute(const Glib::ustring &name, const ValueType &defval) :
    boost::shared_ptr<T>(new T(name, defval))
  {
  }
  RefAttribute(const RefAttribute<T> &refattr) :
    boost::shared_ptr<T>(refattr)
  {
  }
};

class AttributeBool : public Attribute<bool, AttributeBool> {
public:
  inline static bool to_value(const Glib::ustring &str) {
    return str == "true";
  }
  inline static Glib::ustring to_string(const bool &value) {
    return value ? "true" : "false";
  }

  inline AttributeBool(const Glib::ustring &name, bool defval) :
    Attribute<bool, AttributeBool>(name, defval)
  {
  }
};

class AttributeTribool : public Attribute<boost::logic::tribool, AttributeTribool> {
public:
  inline static boost::logic::tribool to_value(const Glib::ustring &str) {
    if (str == "true") {
      return true;
    } else if (str == "indeterminate") {
      return boost::logic::indeterminate;
    } else {
      return false;
    }
  }
  inline static Glib::ustring to_string(const boost::logic::tribool &value) {
    return value ? "true" : (!value ? "false" : "indeterminate");
  }
  inline static bool equal(const boost::logic::tribool &v1,
                           const boost::logic::tribool &v2) {
    using boost::logic::indeterminate;
    return !(indeterminate(v1) != indeterminate(v2) || v1 != v2);
  }

  inline AttributeTribool(const Glib::ustring &name, boost::logic::tribool defval) :
    Attribute<boost::logic::tribool, AttributeTribool>(name, defval)
  {
  }
};

class AttributeStr : public Attribute<Glib::ustring, AttributeStr> {
public:
  inline static Glib::ustring to_value(const Glib::ustring &str) {
    return str;
  }
  inline static Glib::ustring to_string(const Glib::ustring &value) {
    return value;
  }

  template<typename T>
  inline void attach(Glib::PropertyProxy<T> prop) {
    Attribute<Glib::ustring, AttributeStr>::attach(prop);
  }
  template<typename T>
  inline void attach(Glib::PropertyProxy_ReadOnly<T> prop) {
    Attribute<Glib::ustring, AttributeStr>::attach(prop);
  }
  template<typename T>
  inline void attach(T *obj, const char *prop) {
    Attribute<Glib::ustring, AttributeStr>::attach(obj, prop);
  }
  inline void attach(Glib::RefPtr<Gtk::TextBuffer> tb) {
    tb->set_text(get_value());
    conns_.attach(tb);
  }

  inline void detach(Glib::PropertyProxy_Base prop) {
    Attribute<Glib::ustring, AttributeStr>::detach(prop);
  }
  template<typename T>
  inline void detach(T *obj, const char *prop) {
    Attribute<Glib::ustring, AttributeStr>::detach(obj, prop);
  }
  inline void detach(Glib::RefPtr<Gtk::TextBuffer> tb) {
    conns_.detach(tb);
  }

  inline AttributeStr(const Glib::ustring &name,
                      const Glib::ustring &defval) :
    Attribute<Glib::ustring, AttributeStr>(name, defval)
  {
  }
};

template<typename VT, typename DerivedClass>
class AttributeEnum : public Attribute<int, DerivedClass> {
private:
  typedef Attribute<int, DerivedClass> Super;

public:
  typedef VT ValueType;

protected:
  struct NameTable {
    ValueType   value_;
    const char *name_;
  };

public:
  static int to_value(const Glib::ustring &str) {
    for(NameTable *table = DerivedClass::_name_table; table->name_; ++table) {
      if (str == table->name_) return table->value_;
    }
    return DerivedClass::_name_table[0].value_;
  }
  static Glib::ustring to_string(const int &value) {
    const char *fallback = NULL;
    for(NameTable *table = DerivedClass::_name_table; table->name_; ++table) {
      if (!fallback && table->value_ == DerivedClass::_name_table[0].value_) {
        fallback = table->name_;
      }
      if (table->value_ == value) return table->name_;
    }
    return fallback ? fallback : DerivedClass::_name_table[0].name_;
  }

  ValueType get_value() const {
    return static_cast<ValueType>(Super::get_value());
  }
  ValueType get_default_value() const {
    return static_cast<ValueType>(Super::get_default_value());
  }
  void set_value(ValueType value) {
    Super::set_value(value);
  }

  ValueType operator*() const {
    return get_value();
  }
  ValueType operator=(ValueType value) {
    set_value(value);
    return value;
  }

  template<typename T>
  inline void attach(Glib::PropertyProxy<T> prop) {
    prop.set_value(static_cast<ValueType>(get_value()));
    Attribute<int, DerivedClass>::conns_.attach(prop);
  }
  template<typename T>
  inline void attach(Glib::PropertyProxy_ReadOnly<T> prop) {
    Attribute<int, DerivedClass>::conns_.attach(prop);
  }
  inline void detach(Glib::PropertyProxy_Base prop) {
    Attribute<int, DerivedClass>::detach(prop);
  }

  AttributeEnum(const Glib::ustring &name, ValueType defval) :
    Attribute<int, DerivedClass>(name, defval)
  {
  }
};

template<typename T>
class RefAttributeEnum : public boost::shared_ptr<T> {
public:
  typedef typename T::ValueType ValueType;

  ValueType operator*() const {
    return boost::shared_ptr<T>::get()->get_value();
  }
  ValueType operator=(ValueType value) {
    boost::shared_ptr<T>::get()->set_value(value);
    return value;
  }

  RefAttributeEnum() {
  }
  RefAttributeEnum(T *ptr) :
    boost::shared_ptr<T>(ptr)
  {
  }
  RefAttributeEnum(const Glib::ustring &name, ValueType defval) :
    boost::shared_ptr<T>(new T(name, defval))
  {
  }
  RefAttributeEnum(const RefAttributeEnum<T> &refattr) :
    boost::shared_ptr<T>(refattr)
  {
  }
};

template<typename ValueType>
class StandardAttribute : public Attribute<ValueType, StandardAttribute<ValueType> > {
public:
  inline static ValueType to_value(const Glib::ustring &str) {
    return boost::lexical_cast<ValueType>(str);
  }
  inline static Glib::ustring to_string(const ValueType &value) {
    return boost::lexical_cast<Glib::ustring>(value);
  }

  inline StandardAttribute(const Glib::ustring &name, const ValueType &defval) :
    Attribute<ValueType, StandardAttribute<ValueType> >(name, defval)
  {
  }
};

typedef StandardAttribute<int>         AttributeInt;
typedef StandardAttribute<uint>        AttributeUint;
typedef StandardAttribute<double>      AttributeDouble;

typedef RefAttribute<AttributeInt>     RefAttributeInt;
typedef RefAttribute<AttributeUint>    RefAttributeUint;
typedef RefAttribute<AttributeDouble>  RefAttributeDouble;
typedef RefAttribute<AttributeBool>    RefAttributeBool;
typedef RefAttribute<AttributeStr>     RefAttributeStr;
typedef RefAttribute<AttributeTribool> RefAttributeTribool;

}/*conf*/
}/*gpyp*/

#endif/*CONF_ATTRIBUTE_H_*/
